agora inbox for pgsql-hackers@postgresql.orghelp / color / mirror / Atom feed
[PATCH v1 5/5] Use anonymous files to back shared memory segments 319+ messages / 2 participants [nested] [flat]
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 5/5] Use anonymous files to back shared memory segments @ 2024-10-15 14:18 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2024-10-15 14:18 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 47 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 4bdadbb0e2..a01c3e4789 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -103,6 +103,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -116,7 +117,7 @@ static int next_free_slot = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -143,9 +144,9 @@ static int next_free_slot = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -708,6 +709,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_slot), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -725,8 +738,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -762,7 +780,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -777,7 +796,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -793,8 +812,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -813,8 +838,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -903,6 +931,9 @@ AnonymousShmemResize(int newval, void *extra) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); + if (mremap(m->shmem, m->shmem_size, new_size, 0) < 0) elog(LOG, "mremap(%p, %zu) failed: %m", m->shmem, m->shmem_size); diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index 2cd05313b8..50db0da28d 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --ers4zrhjqnhlajas-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v2 6/6] Use anonymous files to back shared memory segments @ 2025-02-23 13:42 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-02-23 13:42 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f5a2bd04000-7f5a32e52000 rw-s 00000000 00:01 1845 /memfd:strategy (deleted) 7f5a39252000-7f5a4030e000 rw-s 00000000 00:01 1842 /memfd:checkpoint (deleted) 7f5a4670e000-7f5a4d7ba000 rw-s 00000000 00:01 1839 /memfd:iocv (deleted) 7f5a53bba000-7f5a5ad26000 rw-s 00000000 00:01 1836 /memfd:descriptors (deleted) 7f5a9ad26000-7f5aa9d94000 rw-s 00000000 00:01 1833 /memfd:buffers (deleted) 7f5d29d94000-7f5d30e00000 rw-s 00000000 00:01 1830 /memfd:main (deleted) * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 46 +++++++++++++++++++++++++++++------ src/include/portability/mem.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 35a8ff92175..8864866f26c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -105,6 +105,7 @@ typedef struct AnonymousMapping void *shmem; /* Pointer to the start of the mapped memory */ void *seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -125,7 +126,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -152,9 +153,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * [...free space...] - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * [...free space...] * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -717,6 +718,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping) void *ptr = MAP_FAILED; int mmap_errno = 0; + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), 0); + #ifndef MAP_HUGETLB /* PGSharedMemoryCreate should have dealt with this case */ Assert(huge_pages != HUGE_PAGES_ON); @@ -734,8 +747,13 @@ CreateAnonymousSegment(AnonymousMapping *mapping) if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); + /* + * Do not use an anonymous file here yet. When adding it, do not forget + * to use ftruncate and flags MFD_HUGETLB & MFD_HUGE_2MB/MFD_HUGE_1GB + * in memfd_create. + */ ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | mmap_flags, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0); mmap_errno = errno; if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED) { @@ -771,7 +789,8 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * - First create the temporary probe mapping of a fixed size and let * kernel to place it at address of its choice. By the virtue of the * probe mapping size we expect it to be located at the lowest - * possible address, expecting some non mapped space above. + * possible address, expecting some non mapped space above. The probe + * is does not need to be backed by an anonymous file. * * - Unmap the probe mapping, remember the address. * @@ -786,7 +805,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping) * without a restart. */ probe = mmap(NULL, PROBE_MAPPING_SIZE, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS | MAP_ANONYMOUS, -1, 0); if (probe == MAP_FAILED) { @@ -802,8 +821,14 @@ CreateAnonymousSegment(AnonymousMapping *mapping) munmap(probe, PROBE_MAPPING_SIZE); + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(probe - offset, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, -1, 0); + PG_MMAP_FLAGS | MAP_FIXED_NOREPLACE, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) { @@ -822,8 +847,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping) */ allocsize = mapping->shmem_size; + /* Specify the segment file size using allocsize. */ + ftruncate(mapping->segment_fd, allocsize); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } @@ -917,6 +945,8 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + ftruncate(m->segment_fd, new_size); /* * Fail hard if faced any issues. In theory we could try to handle this diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED -- 2.45.1 --q7xzg7duksj2kgko-- ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v4 7/8] Use anonymous files to back shared memory segments @ 2025-03-15 15:39 Dmitrii Dolgov <9erthalion6@gmail.com> 0 siblings, 0 replies; 319+ messages in thread From: Dmitrii Dolgov @ 2025-03-15 15:39 UTC (permalink / raw) Allow to use anonymous files for shared memory, instead of plain anonymous memory. Such an anonymous file is created via memfd_create, it lives in memory, behaves like a regular file and semantically equivalent to an anonymous memory allocated via mmap with MAP_ANONYMOUS. Advantages of using anon files are following: * We've got a file descriptor, which could be used for regular file operations (modification, truncation, you name it). * The file could be given a name, which improves readability when it comes to process maps. Here is how it looks like 7f90cde00000-7f90d5126000 rw-s 00000000 00:01 5463 /memfd:main (deleted) 7f90d5126000-7f914de00000 ---p 00000000 00:00 0 7f914de00000-7f9175128000 rw-s 00000000 00:01 5466 /memfd:buffers (deleted) 7f9175128000-7f944de00000 ---p 00000000 00:00 0 7f944de00000-7f9455528000 rw-s 00000000 00:01 5469 /memfd:descriptors (deleted) 7f9455528000-7f94cde00000 ---p 00000000 00:00 0 7f94cde00000-7f94d5228000 rw-s 00000000 00:01 5472 /memfd:iocv (deleted) 7f94d5228000-7f954de00000 ---p 00000000 00:00 0 7f954de00000-7f9555266000 rw-s 00000000 00:01 5475 /memfd:checkpoint (deleted) 7f9555266000-7f958de00000 ---p 00000000 00:00 0 7f958de00000-7f95954aa000 rw-s 00000000 00:01 5478 /memfd:strategy (deleted) 7f95954aa000-7f95cde00000 ---p 00000000 00:00 0 * By default, Linux will not add file-backed shared mappings into a core dump, making it more convenient to work with them in PostgreSQL: no more huge dumps to process. The downside is that memfd_create is Linux specific. --- src/backend/port/sysv_shmem.c | 73 +++++++++++++++++++++++++++++----- src/backend/port/win32_shmem.c | 2 +- src/backend/storage/ipc/ipci.c | 2 +- src/include/portability/mem.h | 2 +- src/include/storage/pg_shmem.h | 3 +- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index a3437973784..87000a24eea 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -107,6 +107,7 @@ typedef struct AnonymousMapping Pointer shmem; /* Pointer to the start of the mapped memory */ Pointer seg_addr; /* SysV shared memory for the header */ unsigned long seg_id; /* IPC key */ + int segment_fd; /* fd for the backing anon file */ } AnonymousMapping; static AnonymousMapping Mappings[ANON_MAPPINGS]; @@ -127,7 +128,7 @@ static int next_free_segment = 0; * 00400000-00490000 /path/bin/postgres * ... * 012d9000-0133e000 [heap] - * 7f443a800000-7f470a800000 /dev/zero (deleted) + * 7f443a800000-7f470a800000 /memfd:main (deleted) * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 * ... @@ -150,9 +151,9 @@ static int next_free_segment = 0; * The result would look like this: * * 012d9000-0133e000 [heap] - * 7f4426f54000-7f442e010000 /dev/zero (deleted) + * 7f4426f54000-7f442e010000 /memfd:main (deleted) * 7f442e010000-7f443a800000 # reserved empty space - * 7f443a800000-7f444196c000 /dev/zero (deleted) + * 7f443a800000-7f444196c000 /memfd:buffers (deleted) * 7f444196c000-7f470a800000 # reserved empty space * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2 @@ -643,13 +644,14 @@ PGSharedMemoryAttach(IpcMemoryId shmId, * *hugepagesize and *mmap_flags are set to 0. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { #ifdef MAP_HUGETLB Size default_hugepagesize = 0; Size hugepagesize_local = 0; int mmap_flags_local = 0; + int memfd_flags_local = 0; /* * System-dependent code to find out the default huge page size. @@ -708,6 +710,7 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) } mmap_flags_local = MAP_HUGETLB; + memfd_flags_local = MFD_HUGETLB; /* * On recent enough Linux, also include the explicit page size, if @@ -718,7 +721,16 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) { int shift = pg_ceil_log2_64(hugepagesize_local); - mmap_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; + } +#endif + +#if defined(MFD_HUGE_MASK) && defined(MFD_HUGE_SHIFT) + if (hugepagesize_local != default_hugepagesize) + { + int shift = pg_ceil_log2_64(hugepagesize_local); + + memfd_flags_local |= (shift & MAP_HUGE_MASK) << MAP_HUGE_SHIFT; } #endif @@ -727,6 +739,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *mmap_flags = mmap_flags_local; if (hugepagesize) *hugepagesize = hugepagesize_local; + if (memfd_flags) + *memfd_flags = memfd_flags_local; #else @@ -734,6 +748,8 @@ GetHugePageSize(Size *hugepagesize, int *mmap_flags) *hugepagesize = 0; if (mmap_flags) *mmap_flags = 0; + if (memfd_flags) + *memfd_flags = 0; #endif /* MAP_HUGETLB */ } @@ -771,7 +787,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Size allocsize = mapping->shmem_size; void *ptr = MAP_FAILED; int mmap_errno = 0; - int mmap_flags = PG_MMAP_FLAGS; + int mmap_flags = PG_MMAP_FLAGS, memfd_flags = 0; #ifndef MAP_HUGETLB /* ReserveAnonymousMemory should have dealt with this case */ @@ -785,7 +801,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY); /* Round up the request size to a suitable large value */ - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, &memfd_flags); if (allocsize % hugepagesize != 0) allocsize += hugepagesize - (allocsize % hugepagesize); @@ -794,6 +810,29 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) } #endif + /* + * Prepare an anonymous file backing the segment. Its size will be + * specified later via ftruncate. + * + * The file behaves like a regular file, but lives in memory. Once all + * references to the file are dropped, it is automatically released. + * Anonymous memory is used for all backing pages of the file, thus it has + * the same semantics as anonymous memory allocations using mmap with the + * MAP_ANONYMOUS flag. + */ + mapping->segment_fd = memfd_create(MappingName(mapping->shmem_segment), + memfd_flags); + + /* + * Specify the segment file size using allocsize, which contains + * potentially modified size. + */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); @@ -807,7 +846,7 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) * a restart. */ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE, - mmap_flags | MAP_FIXED, -1, 0); + mmap_flags | MAP_FIXED, mapping->segment_fd, 0); mmap_errno = errno; if (ptr == MAP_FAILED) @@ -817,8 +856,15 @@ CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base) "fallback to the non-resizable allocation", MappingName(mapping->shmem_segment), allocsize, base + reserved_offset); + /* Specify the segment file size using allocsize. */ + if(ftruncate(mapping->segment_fd, allocsize) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(mapping->shmem_segment)))); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, - PG_MMAP_FLAGS, -1, 0); + PG_MMAP_FLAGS, mapping->segment_fd, 0); mmap_errno = errno; } else @@ -889,7 +935,7 @@ ReserveAnonymousMemory(Size reserve_size) Size hugepagesize, total_size = 0; int mmap_flags; - GetHugePageSize(&hugepagesize, &mmap_flags); + GetHugePageSize(&hugepagesize, &mmap_flags, NULL); /* * Figure out how much memory is needed for all segments, keeping in @@ -1070,6 +1116,13 @@ AnonymousShmemResize(void) if (m->shmem_size == new_size) continue; + /* Resize the backing anon file. */ + if(ftruncate(m->segment_fd, new_size) == -1) + ereport(FATAL, + (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("could not truncase anonymous file for \"%s\": %m", + MappingName(m->shmem_segment)))); + /* Clean up some reserved space to resize into */ if (munmap(m->shmem + m->shmem_size, new_size - m->shmem_size) == -1) ereport(FATAL, diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index ce719f1b412..ba972106de1 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -627,7 +627,7 @@ pgwin32_ReserveSharedMemoryRegion(HANDLE hChild) * use GetLargePageMinimum() instead. */ void -GetHugePageSize(Size *hugepagesize, int *mmap_flags) +GetHugePageSize(Size *hugepagesize, int *mmap_flags, int *memfd_flags) { if (hugepagesize) *hugepagesize = 0; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index abeb91e24fd..dc2b4becf4a 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -396,7 +396,7 @@ InitializeShmemGUCs(void) /* * Calculate the number of huge pages required. */ - GetHugePageSize(&hp_size, NULL); + GetHugePageSize(&hp_size, NULL, NULL); if (hp_size != 0) { Size hp_required; diff --git a/src/include/portability/mem.h b/src/include/portability/mem.h index ef9800732d9..40588ff6968 100644 --- a/src/include/portability/mem.h +++ b/src/include/portability/mem.h @@ -38,7 +38,7 @@ #define MAP_NOSYNC 0 #endif -#define PG_MMAP_FLAGS (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE) +#define PG_MMAP_FLAGS (MAP_SHARED|MAP_HASSEMAPHORE) /* Some really old systems don't define MAP_FAILED. */ #ifndef MAP_FAILED diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index 2e47b222cbb..b9573520d9a 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -124,7 +124,8 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size, PGShmemHeader **shim, Pointer base); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern void PGSharedMemoryDetach(void); -extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags); +extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags, + int *memfd_flags); void *ReserveAnonymousMemory(Size reserve_size); bool ProcessBarrierShmemResize(Barrier *barrier); -- 2.45.1 --vninua6xybvzgrci Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0008-Support-resize-for-hugetlb.patch" ^ permalink raw reply [nested|flat] 319+ messages in thread
* [PATCH v1 2/3] remove support for PQfn @ 2026-05-22 19:00 Nathan Bossart <nathan@postgresql.org> 0 siblings, 0 replies; 319+ messages in thread From: Nathan Bossart @ 2026-05-22 19:00 UTC (permalink / raw) --- doc/src/sgml/libpq.sgml | 119 +------------- src/backend/tcop/fastpath.c | 3 +- src/include/tcop/dest.h | 4 +- src/interfaces/libpq/fe-exec.c | 54 +------ src/interfaces/libpq/fe-protocol3.c | 242 ---------------------------- src/interfaces/libpq/libpq-int.h | 5 - 6 files changed, 12 insertions(+), 415 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 7d3c3bb66d8..812e9089bfd 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -3738,7 +3738,7 @@ PGresult *PQdescribePrepared(PGconn *conn, const char *stmtName); <xref linkend="libpq-PQparamtype"/> can be applied to this <structname>PGresult</structname> to obtain information about the parameters of the prepared statement, and the functions - <xref linkend="libpq-PQnfields"/>, <xref linkend="libpq-PQfname"/>, + <xref linkend="libpq-PQnfields"/>, <xref linkend="libpq-PQftype"/>, etc. provide information about the result columns (if any) of the statement. </para> @@ -5887,7 +5887,7 @@ int PQflush(PGconn *conn); are permitted, command strings containing multiple SQL commands are disallowed, and so is <literal>COPY</literal>. Using synchronous command execution functions - such as <function>PQfn</function>, + such as <function>PQexec</function>, <function>PQexecParams</function>, <function>PQprepare</function>, @@ -7046,121 +7046,6 @@ int PQrequestCancel(PGconn *conn); </sect2> </sect1> - <sect1 id="libpq-fastpath"> - <title>The Fast-Path Interface</title> - - <indexterm zone="libpq-fastpath"> - <primary>fast path</primary> - </indexterm> - - <para> - <productname>PostgreSQL</productname> provides a fast-path interface - to send simple function calls to the server. - </para> - - <warning> - <para> - This interface is unsafe and should not be used. When - <parameter>result_is_int</parameter> is set to <literal>0</literal>, - <function>PQfn</function> may write data beyond the end of - <parameter>result_buf</parameter>, regardless of whether the buffer has - enough space for the requested number of bytes. Furthermore, it is - obsolete, as one can achieve similar - performance and greater functionality by setting up a prepared - statement to define the function call. Then, executing the statement - with binary transmission of parameters and results substitutes for a - fast-path function call. - </para> - </warning> - - <para> - The function <function id="libpq-PQfn">PQfn</function><indexterm><primary>PQfn</primary></indexterm> - requests execution of a server function via the fast-path interface: -<synopsis> -PGresult *PQfn(PGconn *conn, - int fnid, - int *result_buf, - int *result_len, - int result_is_int, - const PQArgBlock *args, - int nargs); - -typedef struct -{ - int len; - int isint; - union - { - int *ptr; - int integer; - } u; -} PQArgBlock; -</synopsis> - </para> - - <para> - The <parameter>fnid</parameter> argument is the OID of the function to be - executed. <parameter>args</parameter> and <parameter>nargs</parameter> define the - parameters to be passed to the function; they must match the declared - function argument list. When the <parameter>isint</parameter> field of a - parameter structure is true, the <parameter>u.integer</parameter> value is sent - to the server as an integer of the indicated length (this must be - 2 or 4 bytes); proper byte-swapping occurs. When <parameter>isint</parameter> - is false, the indicated number of bytes at <parameter>*u.ptr</parameter> are - sent with no processing; the data must be in the format expected by - the server for binary transmission of the function's argument data - type. (The declaration of <parameter>u.ptr</parameter> as being of - type <type>int *</type> is historical; it would be better to consider - it <type>void *</type>.) - <parameter>result_buf</parameter> points to the buffer in which to place - the function's return value. The caller must have allocated sufficient - space to store the return value. (There is no check!) The actual result - length in bytes will be returned in the integer pointed to by - <parameter>result_len</parameter>. If a 2- or 4-byte integer result - is expected, set <parameter>result_is_int</parameter> to 1, otherwise - set it to 0. Setting <parameter>result_is_int</parameter> to 1 causes - <application>libpq</application> to byte-swap the value if necessary, so that it - is delivered as a proper <type>int</type> value for the client machine; - note that a 4-byte integer is delivered into <parameter>*result_buf</parameter> - for either allowed result size. - When <parameter>result_is_int</parameter> is 0, the binary-format byte string - sent by the server is returned unmodified. (In this case it's better - to consider <parameter>result_buf</parameter> as being of - type <type>void *</type>.) - </para> - - <para> - <function>PQfn</function> always returns a valid - <structname>PGresult</structname> pointer, with - status <literal>PGRES_COMMAND_OK</literal> for success - or <literal>PGRES_FATAL_ERROR</literal> if some problem was encountered. - The result status should be - checked before the result is used. The caller is responsible for - freeing the <structname>PGresult</structname> with - <xref linkend="libpq-PQclear"/> when it is no longer needed. - </para> - - <para> - To pass a NULL argument to the function, set - the <parameter>len</parameter> field of that parameter structure - to <literal>-1</literal>; the <parameter>isint</parameter> - and <parameter>u</parameter> fields are then irrelevant. - </para> - - <para> - If the function returns NULL, <parameter>*result_len</parameter> is set - to <literal>-1</literal>, and <parameter>*result_buf</parameter> is not - modified. - </para> - - <para> - Note that it is not possible to handle set-valued results when using - this interface. Also, the function must be a plain function, not an - aggregate, window function, or procedure. - </para> - - </sect1> - <sect1 id="libpq-notify"> <title>Asynchronous Notification</title> diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c index 52772bc90a8..2a5c45efffe 100644 --- a/src/backend/tcop/fastpath.c +++ b/src/backend/tcop/fastpath.c @@ -11,7 +11,8 @@ * src/backend/tcop/fastpath.c * * NOTES - * This cruft is the server side of PQfn. + * This cruft is the server side of PQfn, which was removed in v20 but + * may still be used by older clients. * *------------------------------------------------------------------------- */ diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h index 103f27fc3cb..507414421ec 100644 --- a/src/include/tcop/dest.h +++ b/src/include/tcop/dest.h @@ -12,8 +12,8 @@ * * - a remote process is the destination when we are * running a backend with a frontend and the frontend executes - * PQexec() or PQfn(). In this case, the results are sent - * to the frontend via the functions in backend/libpq. + * PQexec(). In this case, the results are sent to the frontend via + * the functions in backend/libpq. * * - DestNone is the destination when the system executes * a query internally. The results are discarded. diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 7b8edacbfde..400e1eef94b 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -2986,13 +2986,11 @@ PQendcopy(PGconn *conn) * nargs : # of arguments in args array. * * RETURNS - * PGresult with status = PGRES_COMMAND_OK if successful. - * *result_len is > 0 if there is a return value, 0 if not. - * PGresult with status = PGRES_FATAL_ERROR if backend returns an error. - * NULL on communications failure. conn->errorMessage will be set. + * This function was unsafe and is no longer supported, so it now always + * sets *result_len to 0 and returns a PGresult with status set to + * PGRES_FATAL_ERROR. * ---------------- */ - PGresult * PQfn(PGconn *conn, int fnid, @@ -3001,51 +2999,11 @@ PQfn(PGconn *conn, int result_is_int, const PQArgBlock *args, int nargs) -{ - return PQnfn(conn, fnid, result_buf, -1, result_len, - result_is_int, args, nargs); -} - -/* - * PQnfn - * Private version of PQfn() with verification that returned data fits in - * result_buf when result_is_int == 0. Setting buf_size to -1 disables - * this verification. - */ -PGresult * -PQnfn(PGconn *conn, int fnid, int *result_buf, int buf_size, int *result_len, - int result_is_int, const PQArgBlock *args, int nargs) { *result_len = 0; - - if (!conn) - return NULL; - - /* - * Since this is the beginning of a query cycle, reset the error state. - * However, in pipeline mode with something already queued, the error - * buffer belongs to that command and we shouldn't clear it. - */ - if (conn->cmd_queue_head == NULL) - pqClearConnErrorState(conn); - - if (conn->pipelineStatus != PQ_PIPELINE_OFF) - { - libpq_append_conn_error(conn, "%s not allowed in pipeline mode", "PQfn"); - return NULL; - } - - if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE || - pgHavePendingResult(conn)) - { - libpq_append_conn_error(conn, "connection in wrong state"); - return NULL; - } - - return pqFunctionCall3(conn, fnid, - result_buf, buf_size, result_len, - result_is_int, - args, nargs); + libpq_append_conn_error(conn, "PQfn() is no longer supported"); + pqSaveErrorResult(conn); + return pqPrepareAsyncResult(conn); } /* ====== Pipeline mode support ======== */ diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 840e018cd18..08910a459c0 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -2196,248 +2196,6 @@ pqEndcopy3(PGconn *conn) return 1; } - -/* - * PQfn - Send a function call to the POSTGRES backend. - * - * See fe-exec.c for documentation. - */ -PGresult * -pqFunctionCall3(PGconn *conn, Oid fnid, - int *result_buf, int buf_size, int *actual_result_len, - int result_is_int, - const PQArgBlock *args, int nargs) -{ - bool needInput = false; - ExecStatusType status = PGRES_FATAL_ERROR; - char id; - int msgLength; - int avail; - int i; - - /* already validated by PQfn */ - Assert(conn->pipelineStatus == PQ_PIPELINE_OFF); - - /* PQfn already validated connection state */ - - if (pqPutMsgStart(PqMsg_FunctionCall, conn) < 0 || - pqPutInt(fnid, 4, conn) < 0 || /* function id */ - pqPutInt(1, 2, conn) < 0 || /* # of format codes */ - pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */ - pqPutInt(nargs, 2, conn) < 0) /* # of args */ - { - /* error message should be set up already */ - return NULL; - } - - for (i = 0; i < nargs; ++i) - { /* len.int4 + contents */ - if (pqPutInt(args[i].len, 4, conn)) - return NULL; - if (args[i].len == -1) - continue; /* it's NULL */ - - if (args[i].isint) - { - if (pqPutInt(args[i].u.integer, args[i].len, conn)) - return NULL; - } - else - { - if (pqPutnchar(args[i].u.ptr, args[i].len, conn)) - return NULL; - } - } - - if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */ - return NULL; - - if (pqPutMsgEnd(conn) < 0 || - pqFlush(conn)) - return NULL; - - for (;;) - { - if (needInput) - { - /* Wait for some data to arrive (or for the channel to close) */ - if (pqWait(true, false, conn) || - pqReadData(conn) < 0) - break; - } - - /* - * Scan the message. If we run out of data, loop around to try again. - */ - needInput = true; - - conn->inCursor = conn->inStart; - if (pqGetc(&id, conn)) - continue; - if (pqGetInt(&msgLength, 4, conn)) - continue; - - /* - * Try to validate message type/length here. A length less than 4 is - * definitely broken. Large lengths should only be believed for a few - * message types. - */ - if (msgLength < 4) - { - handleSyncLoss(conn, id, msgLength); - break; - } - if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) - { - handleSyncLoss(conn, id, msgLength); - break; - } - - /* - * Can't process if message body isn't all here yet. - */ - msgLength -= 4; - avail = conn->inEnd - conn->inCursor; - if (avail < msgLength) - { - /* - * Before looping, enlarge the input buffer if needed to hold the - * whole message. See notes in parseInput. - */ - if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, - conn)) - { - /* - * Abandon the connection. There's not much else we can - * safely do; we can't just ignore the message or we could - * miss important changes to the connection state. - * pqCheckInBufferSpace() already reported the error. - */ - handleFatalError(conn); - break; - } - continue; - } - - /* - * We should see V or E response to the command, but might get N - * and/or A notices first. We also need to swallow the final Z before - * returning. - */ - switch (id) - { - case PqMsg_FunctionCallResponse: - if (pqGetInt(actual_result_len, 4, conn)) - continue; - if (*actual_result_len != -1) - { - if (result_is_int) - { - if (pqGetInt(result_buf, *actual_result_len, conn)) - continue; - } - else - { - /* - * If the server returned too much data for the - * buffer, something fishy is going on. Abandon ship. - */ - if (buf_size != -1 && *actual_result_len > buf_size) - { - libpq_append_conn_error(conn, "server returned too much data"); - handleFatalError(conn); - return pqPrepareAsyncResult(conn); - } - - if (pqGetnchar(result_buf, - *actual_result_len, - conn)) - continue; - } - } - /* correctly finished function result message */ - status = PGRES_COMMAND_OK; - break; - case PqMsg_ErrorResponse: - if (pqGetErrorNotice3(conn, true)) - continue; - status = PGRES_FATAL_ERROR; - break; - case PqMsg_NotificationResponse: - /* handle notify and go back to processing return values */ - if (getNotify(conn)) - continue; - break; - case PqMsg_NoticeResponse: - /* handle notice and go back to processing return values */ - if (pqGetErrorNotice3(conn, false)) - continue; - break; - case PqMsg_ReadyForQuery: - if (getReadyForQuery(conn)) - continue; - - /* consume the message */ - pqParseDone(conn, conn->inStart + 5 + msgLength); - - /* - * If we already have a result object (probably an error), use - * that. Otherwise, if we saw a function result message, - * report COMMAND_OK. Otherwise, the backend violated the - * protocol, so complain. - */ - if (!pgHavePendingResult(conn)) - { - if (status == PGRES_COMMAND_OK) - { - conn->result = PQmakeEmptyPGresult(conn, status); - if (!conn->result) - { - libpq_append_conn_error(conn, "out of memory"); - pqSaveErrorResult(conn); - } - } - else - { - libpq_append_conn_error(conn, "protocol error: no function result"); - pqSaveErrorResult(conn); - } - } - /* and we're out */ - return pqPrepareAsyncResult(conn); - case PqMsg_ParameterStatus: - if (getParameterStatus(conn)) - continue; - break; - default: - /* The backend violates the protocol. */ - libpq_append_conn_error(conn, "protocol error: id=0x%x", id); - pqSaveErrorResult(conn); - - /* - * We can't call parsing done due to the protocol violation - * (so message tracing wouldn't work), but trust the specified - * message length as what to skip. - */ - conn->inStart += 5 + msgLength; - return pqPrepareAsyncResult(conn); - } - - /* Completed parsing this message, keep going */ - pqParseDone(conn, conn->inStart + 5 + msgLength); - needInput = false; - } - - /* - * We fall out of the loop only upon failing to read data. - * conn->errorMessage has been set by pqWait or pqReadData. We want to - * append it to any already-received error message. - */ - pqSaveErrorResult(conn); - return pqPrepareAsyncResult(conn); -} - - /* * Construct startup packet * diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 5248e895bc0..9dd0f42b6b7 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -764,11 +764,6 @@ extern int pqGetCopyData3(PGconn *conn, char **buffer, int async); extern int pqGetline3(PGconn *conn, char *s, int maxlen); extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize); extern int pqEndcopy3(PGconn *conn); -extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid, - int *result_buf, int buf_size, - int *actual_result_len, - int result_is_int, - const PQArgBlock *args, int nargs); /* === in fe-cancel.c === */ -- 2.50.1 (Apple Git-155) --NDf4SqMA2rj1ZNJ4 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v1-0003-LO-frontend-PQexecParams.patch ^ permalink raw reply [nested|flat] 319+ messages in thread
end of thread, other threads:[~2026-05-22 19:00 UTC | newest] Thread overview: 319+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2024-10-15 14:18 [PATCH v1 5/5] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-02-23 13:42 [PATCH v2 6/6] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2025-03-15 15:39 [PATCH v4 7/8] Use anonymous files to back shared memory segments Dmitrii Dolgov <9erthalion6@gmail.com> 2026-05-22 19:00 [PATCH v1 2/3] remove support for PQfn Nathan Bossart <nathan@postgresql.org>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox