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 v47 9/9] Teach snapshot builder to skip transactions running REPACK (CONCURRENTLY). @ 2026-03-17 19:22 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 319+ messages in thread From: Antonin Houska @ 2026-03-17 19:22 UTC (permalink / raw) During logical decoding, we need to know if particular transaction performed catalog changes because catalog information is needed to construct heap tuples. To be sure that we have enough information of each transaction, the logical decoding cannot start before all the already running transactions have completed. The problem with REPACK (CONCURRENTLY) is that it has XID assigned and writes WAL records marked with it. Thus if another backend runs REPACK (CONCURRENTLY) and tries to setup the logical decoding, it has to wait for the completion of all the other transactions involved in REPACK (CONCURRENTLY). However, REPACK (CONCURRENTLY) does not perform any catalog changes relevant to logical decoding, so the other backends executing this command can ignore it. This patch implements it by adding information about transactions executing the command to the xl_running_xacts WAL record, and by teaching the snapshot builder to use the information. --- src/backend/access/rmgrdesc/standbydesc.c | 15 ++++- src/backend/access/transam/xlog.c | 2 + src/backend/commands/repack.c | 16 +++++ src/backend/replication/logical/snapbuild.c | 38 ++++++++--- src/backend/storage/ipc/procarray.c | 74 +++++++++++++++++++-- src/backend/storage/ipc/standby.c | 8 ++- src/include/storage/standby.h | 3 + src/include/storage/standbydefs.h | 2 + 8 files changed, 137 insertions(+), 21 deletions(-) diff --git a/src/backend/access/rmgrdesc/standbydesc.c b/src/backend/access/rmgrdesc/standbydesc.c index 0a291354ae2..b96b73d51d3 100644 --- a/src/backend/access/rmgrdesc/standbydesc.c +++ b/src/backend/access/rmgrdesc/standbydesc.c @@ -21,10 +21,11 @@ standby_desc_running_xacts(StringInfo buf, xl_running_xacts *xlrec) { int i; - appendStringInfo(buf, "nextXid %u latestCompletedXid %u oldestRunningXid %u", + appendStringInfo(buf, "nextXid %u latestCompletedXid %u oldestRunningXid %u oldestRunningXidLogical %u", xlrec->nextXid, xlrec->latestCompletedXid, - xlrec->oldestRunningXid); + xlrec->oldestRunningXid, + xlrec->oldestRunningXidLogical); if (xlrec->xcnt > 0) { appendStringInfo(buf, "; %d xacts:", xlrec->xcnt); @@ -41,6 +42,16 @@ standby_desc_running_xacts(StringInfo buf, xl_running_xacts *xlrec) for (i = 0; i < xlrec->subxcnt; i++) appendStringInfo(buf, " %u", xlrec->xids[xlrec->xcnt + i]); } + + if (xlrec->xcnt_repack > 0) + { + TransactionId *xids_repack; + + appendStringInfo(buf, "; %d xacts_repack:", xlrec->xcnt_repack); + xids_repack = xlrec->xids + xlrec->xcnt + xlrec->subxcnt; + for (i = 0; i < xlrec->xcnt_repack; i++) + appendStringInfo(buf, " %u", xids_repack[i]); + } } void diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 2c1c6f88b74..f1cbdee7618 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -5912,6 +5912,7 @@ StartupXLOG(void) * subxids are listed with their parent prepared transactions. */ running.xcnt = nxids; + running.xcnt_repack = 0; running.subxcnt = 0; running.subxid_status = SUBXIDS_IN_SUBTRANS; running.nextXid = XidFromFullTransactionId(checkPoint.nextXid); @@ -8483,6 +8484,7 @@ xlog_redo(XLogReaderState *record) * with their parent prepared transactions. */ running.xcnt = nxids; + running.xcnt_repack = 0; running.subxcnt = 0; running.subxid_status = SUBXIDS_IN_SUBTRANS; running.nextXid = XidFromFullTransactionId(checkPoint.nextXid); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 3acb2e1d2ba..74faa4f5582 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -988,6 +988,22 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, if (concurrent) { + /* + * Do not let other backends wait for our completion during their + * setup of logical replication. Unlike logical replication publisher, + * we will have XID assigned, so the other backends - whether + * walsenders involved in logical replication or regular backends + * executing also REPACK (CONCURRENTLY) - would have to wait for our + * completion before they can build their initial snapshot. It is + * o.k. for any decoding backend to ignore us because we do not change + * tuple descriptor of any table, and the data changes we write should + * not be decoded by other backends. + */ + LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); + MyProc->statusFlags |= PROC_IN_CONCURRENT_REPACK; + ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags; + LWLockRelease(ProcArrayLock); + /* * The worker needs to be member of the locking group we're the leader * of. We ought to become the leader before the worker starts. The diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c index 9cf499ce7c6..fbdd4600a2b 100644 --- a/src/backend/replication/logical/snapbuild.c +++ b/src/backend/replication/logical/snapbuild.c @@ -1172,7 +1172,7 @@ SnapBuildProcessRunningXacts(SnapBuild *builder, XLogRecPtr lsn, xl_running_xact * xmin, which looks odd but is correct and actually more efficient, since * we hit fast paths in heapam_visibility.c. */ - builder->xmin = running->oldestRunningXid; + builder->xmin = running->oldestRunningXidLogical; /* Remove transactions we don't need to keep track off anymore */ SnapBuildPurgeOlderTxn(builder); @@ -1188,9 +1188,9 @@ SnapBuildProcessRunningXacts(SnapBuild *builder, XLogRecPtr lsn, xl_running_xact */ xmin = ReorderBufferGetOldestXmin(builder->reorder); if (xmin == InvalidTransactionId) - xmin = running->oldestRunningXid; + xmin = running->oldestRunningXidLogical; elog(DEBUG3, "xmin: %u, xmax: %u, oldest running: %u, oldest xmin: %u", - builder->xmin, builder->xmax, running->oldestRunningXid, xmin); + builder->xmin, builder->xmax, running->oldestRunningXidLogical, xmin); LogicalIncreaseXminForSlot(lsn, xmin); /* @@ -1275,14 +1275,14 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn * have all necessary catalog rows anymore. */ if (TransactionIdIsNormal(builder->initial_xmin_horizon) && - NormalTransactionIdPrecedes(running->oldestRunningXid, + NormalTransactionIdPrecedes(running->oldestRunningXidLogical, builder->initial_xmin_horizon)) { ereport(DEBUG1, errmsg_internal("skipping snapshot at %X/%08X while building logical decoding snapshot, xmin horizon too low", LSN_FORMAT_ARGS(lsn)), errdetail_internal("initial xmin horizon of %u vs the snapshot's %u", - builder->initial_xmin_horizon, running->oldestRunningXid)); + builder->initial_xmin_horizon, running->oldestRunningXidLogical)); SnapBuildWaitSnapshot(running, builder->initial_xmin_horizon); @@ -1299,7 +1299,7 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn * NB: We might have already started to incrementally assemble a snapshot, * so we need to be careful to deal with that. */ - if (running->oldestRunningXid == running->nextXid) + if (running->oldestRunningXidLogical == running->nextXid) { if (!XLogRecPtrIsValid(builder->start_decoding_at) || builder->start_decoding_at <= lsn) @@ -1378,14 +1378,14 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn /* * c) transition from BUILDING_SNAPSHOT to FULL_SNAPSHOT. * - * In BUILDING_SNAPSHOT state, and this xl_running_xacts' oldestRunningXid + * In BUILDING_SNAPSHOT state, and this xl_running_xacts' oldestRunningXidLogical * is >= than nextXid from when we switched to BUILDING_SNAPSHOT. This * means all transactions starting afterwards have enough information to * be decoded. Switch to FULL_SNAPSHOT. */ else if (builder->state == SNAPBUILD_BUILDING_SNAPSHOT && TransactionIdPrecedesOrEquals(builder->next_phase_at, - running->oldestRunningXid)) + running->oldestRunningXidLogical)) { builder->state = SNAPBUILD_FULL_SNAPSHOT; builder->next_phase_at = running->nextXid; @@ -1402,14 +1402,14 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn /* * c) transition from FULL_SNAPSHOT to CONSISTENT. * - * In FULL_SNAPSHOT state, and this xl_running_xacts' oldestRunningXid is + * In FULL_SNAPSHOT state, and this xl_running_xacts' oldestRunningXidLogical is * >= than nextXid from when we switched to FULL_SNAPSHOT. This means all * transactions that are currently in progress have a catalog snapshot, * and all their changes have been collected. Switch to CONSISTENT. */ else if (builder->state == SNAPBUILD_FULL_SNAPSHOT && TransactionIdPrecedesOrEquals(builder->next_phase_at, - running->oldestRunningXid)) + running->oldestRunningXidLogical)) { builder->state = SNAPBUILD_CONSISTENT; builder->next_phase_at = InvalidTransactionId; @@ -1459,6 +1459,24 @@ SnapBuildWaitSnapshot(xl_running_xacts *running, TransactionId cutoff) if (TransactionIdFollows(xid, cutoff)) continue; + /* Do not wait for transactions running REPACK (CONCURRENTLY). */ + if (running->xcnt_repack > 0) + { + TransactionId *xids_repack; + int i; + + xids_repack = running->xids + running->xcnt + running->subxcnt; + + for (i = 0; i < running->xcnt_repack; i++) + { + if (xid == xids_repack[i]) + break; + } + /* Found? */ + if (i < running->xcnt_repack) + continue; + } + XactLockTableWait(xid, NULL, NULL, XLTW_None); } diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index cc207cb56e3..6dd4fa7dcc0 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -2643,15 +2643,24 @@ GetRunningTransactionData(void) RunningTransactions CurrentRunningXacts = &CurrentRunningXactsData; TransactionId latestCompletedXid; TransactionId oldestRunningXid; + TransactionId oldestRunningXidLogical; TransactionId oldestDatabaseRunningXid; TransactionId *xids; int index; - int count; + int count, count_repack; int subcount; bool suboverflowed; + TransactionId *xids_repack = NULL; + bool logical_decoding_enabled = IsLogicalDecodingEnabled(); Assert(!RecoveryInProgress()); + /* + * TODO Consider a GUC to reserve certain amount of replication slots for + * REPACK (CONCURRENTLY) and using it here. + */ +#define MAX_REPACK_XIDS 16 + /* * Allocating space for maxProcs xids is usually overkill; numProcs would * be sufficient. But it seems better to do the malloc while not holding @@ -2663,11 +2672,14 @@ GetRunningTransactionData(void) */ if (CurrentRunningXacts->xids == NULL) { + /* FIXME probably fails if logical decoding is enable on-the-fly */ + int nrepack = logical_decoding_enabled ? MAX_REPACK_XIDS : 0; + /* * First call */ CurrentRunningXacts->xids = (TransactionId *) - malloc(TOTAL_MAX_CACHED_SUBXIDS * sizeof(TransactionId)); + malloc((TOTAL_MAX_CACHED_SUBXIDS + nrepack) * sizeof(TransactionId)); if (CurrentRunningXacts->xids == NULL) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), @@ -2676,7 +2688,10 @@ GetRunningTransactionData(void) xids = CurrentRunningXacts->xids; - count = subcount = 0; + if (logical_decoding_enabled) + xids_repack = palloc_array(TransactionId, MAX_REPACK_XIDS); + + count = subcount = count_repack = 0; suboverflowed = false; /* @@ -2688,7 +2703,7 @@ GetRunningTransactionData(void) latestCompletedXid = XidFromFullTransactionId(TransamVariables->latestCompletedXid); - oldestDatabaseRunningXid = oldestRunningXid = + oldestDatabaseRunningXid = oldestRunningXid = oldestRunningXidLogical = XidFromFullTransactionId(TransamVariables->nextXid); /* @@ -2697,6 +2712,8 @@ GetRunningTransactionData(void) for (index = 0; index < arrayP->numProcs; index++) { TransactionId xid; + int pgprocno; + PGPROC *proc; /* Fetch xid just once - see GetNewTransactionId */ xid = UINT32_ACCESS_ONCE(other_xids[index]); @@ -2716,6 +2733,21 @@ GetRunningTransactionData(void) if (TransactionIdPrecedes(xid, oldestRunningXid)) oldestRunningXid = xid; + if (logical_decoding_enabled && + TransactionIdPrecedes(xid, oldestRunningXidLogical)) + { + /* + * Backends running REPACK concurrently need to be excluded from + * oldestRunningXidLogical, otherwise the snapshot builder cannot + * proceed in building the initial snapshot. + */ + pgprocno = arrayP->pgprocnos[index]; + proc = &allProcs[pgprocno]; + + if ((proc->statusFlags & PROC_IN_CONCURRENT_REPACK) == 0) + oldestRunningXidLogical = xid; + } + /* * Also, update the oldest running xid within the current database. As * fetching pgprocno and PGPROC could cause cache misses, we do cheap @@ -2723,8 +2755,8 @@ GetRunningTransactionData(void) */ if (TransactionIdPrecedes(xid, oldestDatabaseRunningXid)) { - int pgprocno = arrayP->pgprocnos[index]; - PGPROC *proc = &allProcs[pgprocno]; + pgprocno = arrayP->pgprocnos[index]; + proc = &allProcs[pgprocno]; if (proc->databaseId == MyDatabaseId) oldestDatabaseRunningXid = xid; @@ -2742,6 +2774,19 @@ GetRunningTransactionData(void) */ xids[count++] = xid; + + /* + * Collect XIDSs of transactions running REPACK (CONCURRENTLY). + */ + if (logical_decoding_enabled && + count_repack < MAX_REPACK_XIDS) + { + pgprocno = arrayP->pgprocnos[index]; + proc = &allProcs[pgprocno]; + + if (proc->statusFlags & PROC_IN_CONCURRENT_REPACK) + xids_repack[count_repack++] = xid; + } } /* @@ -2782,6 +2827,19 @@ GetRunningTransactionData(void) } } + /* + * Append the XIDs running REPACK (CONCURRENTLY), if any. + * + * XXX Should we sort the array and use bsearch() when using it? + */ + if (count_repack > 0) + { + for (int i = 0; i < count_repack; i++) + xids[count++] = xids_repack[i]; + } + if (xids_repack) + pfree(xids_repack); + /* * It's important *not* to include the limits set by slots here because * snapbuild.c uses oldestRunningXid to manage its xmin horizon. If those @@ -2791,11 +2849,13 @@ GetRunningTransactionData(void) * increases if slots do. */ - CurrentRunningXacts->xcnt = count - subcount; + CurrentRunningXacts->xcnt = count - subcount - count_repack; CurrentRunningXacts->subxcnt = subcount; + CurrentRunningXacts->xcnt_repack = count_repack; CurrentRunningXacts->subxid_status = suboverflowed ? SUBXIDS_IN_SUBTRANS : SUBXIDS_IN_ARRAY; CurrentRunningXacts->nextXid = XidFromFullTransactionId(TransamVariables->nextXid); CurrentRunningXacts->oldestRunningXid = oldestRunningXid; + CurrentRunningXacts->oldestRunningXidLogical = oldestRunningXidLogical; CurrentRunningXacts->oldestDatabaseRunningXid = oldestDatabaseRunningXid; CurrentRunningXacts->latestCompletedXid = latestCompletedXid; diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c index de9092fdf5b..fd3e4fe31f3 100644 --- a/src/backend/storage/ipc/standby.c +++ b/src/backend/storage/ipc/standby.c @@ -1189,6 +1189,7 @@ standby_redo(XLogReaderState *record) RunningTransactionsData running; running.xcnt = xlrec->xcnt; + running.xcnt_repack = xlrec->xcnt_repack; running.subxcnt = xlrec->subxcnt; running.subxid_status = xlrec->subxid_overflow ? SUBXIDS_MISSING : SUBXIDS_IN_ARRAY; running.nextXid = xlrec->nextXid; @@ -1359,10 +1360,12 @@ LogCurrentRunningXacts(RunningTransactions CurrRunningXacts) XLogRecPtr recptr; xlrec.xcnt = CurrRunningXacts->xcnt; + xlrec.xcnt_repack = CurrRunningXacts->xcnt_repack; xlrec.subxcnt = CurrRunningXacts->subxcnt; xlrec.subxid_overflow = (CurrRunningXacts->subxid_status != SUBXIDS_IN_ARRAY); xlrec.nextXid = CurrRunningXacts->nextXid; xlrec.oldestRunningXid = CurrRunningXacts->oldestRunningXid; + xlrec.oldestRunningXidLogical = CurrRunningXacts->oldestRunningXidLogical; xlrec.latestCompletedXid = CurrRunningXacts->latestCompletedXid; /* Header */ @@ -1371,9 +1374,10 @@ LogCurrentRunningXacts(RunningTransactions CurrRunningXacts) XLogRegisterData(&xlrec, MinSizeOfXactRunningXacts); /* array of TransactionIds */ - if (xlrec.xcnt > 0) + if (xlrec.xcnt + xlrec.xcnt_repack > 0) XLogRegisterData(CurrRunningXacts->xids, - (xlrec.xcnt + xlrec.subxcnt) * sizeof(TransactionId)); + (xlrec.xcnt + xlrec.xcnt_repack + xlrec.subxcnt) * + sizeof(TransactionId)); recptr = XLogInsert(RM_STANDBY_ID, XLOG_RUNNING_XACTS); diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h index 6a314c693cd..9e6e7b18786 100644 --- a/src/include/storage/standby.h +++ b/src/include/storage/standby.h @@ -127,10 +127,13 @@ typedef enum typedef struct RunningTransactionsData { int xcnt; /* # of xact ids in xids[] */ + int xcnt_repack; /* # of xacts running REPACK + * (CONCURRENTLY). */ int subxcnt; /* # of subxact ids in xids[] */ subxids_array_status subxid_status; TransactionId nextXid; /* xid from TransamVariables->nextXid */ TransactionId oldestRunningXid; /* *not* oldestXmin */ + TransactionId oldestRunningXidLogical; TransactionId oldestDatabaseRunningXid; /* same as above, but within the * current database */ TransactionId latestCompletedXid; /* so we can set xmax */ diff --git a/src/include/storage/standbydefs.h b/src/include/storage/standbydefs.h index 231d251fd51..edad609fa9a 100644 --- a/src/include/storage/standbydefs.h +++ b/src/include/storage/standbydefs.h @@ -47,10 +47,12 @@ typedef struct xl_standby_locks typedef struct xl_running_xacts { int xcnt; /* # of xact ids in xids[] */ + int xcnt_repack; /* # of xacts running REPACK (CONCURRENTLY) */ int subxcnt; /* # of subxact ids in xids[] */ bool subxid_overflow; /* snapshot overflowed, subxids missing */ TransactionId nextXid; /* xid from TransamVariables->nextXid */ TransactionId oldestRunningXid; /* *not* oldestXmin */ + TransactionId oldestRunningXidLogical; TransactionId latestCompletedXid; /* so we can set xmax */ TransactionId xids[FLEXIBLE_ARRAY_MEMBER]; -- 2.47.3 --2fxmamo6mu2qbxgv-- ^ permalink raw reply [nested|flat] 319+ messages in thread
end of thread, other threads:[~2026-03-17 19:22 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-03-17 19:22 [PATCH v47 9/9] Teach snapshot builder to skip transactions running REPACK (CONCURRENTLY). Antonin Houska <ah@cybertec.at>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox