agora inbox for pgsql-hackers@postgresql.org
help / 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 v50 2/8] Rename cluster.c/h -> repack.c/h
@ 2026-03-31 16:55 Álvaro Herrera <alvherre@kurilemu.de>
0 siblings, 0 replies; 319+ messages in thread
From: Álvaro Herrera @ 2026-03-31 16:55 UTC (permalink / raw)
---
src/backend/commands/Makefile | 2 +-
src/backend/commands/matview.c | 2 +-
src/backend/commands/meson.build | 2 +-
src/backend/commands/{cluster.c => repack.c} | 6 +++---
src/backend/commands/tablecmds.c | 2 +-
src/backend/commands/vacuum.c | 6 +++---
src/backend/storage/ipc/procsignal.c | 1 +
src/backend/tcop/postgres.c | 1 +
src/backend/tcop/utility.c | 2 +-
src/include/commands/{cluster.h => repack.h} | 12 ++++++------
10 files changed, 19 insertions(+), 17 deletions(-)
rename src/backend/commands/{cluster.c => repack.c} (99%)
rename src/include/commands/{cluster.h => repack.h} (90%)
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index c10fdba2bbb..fe1bba3a9b9 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,6 @@ OBJS = \
amcmds.o \
analyze.o \
async.o \
- cluster.o \
collationcmds.o \
comment.o \
constraint.o \
@@ -51,6 +50,7 @@ OBJS = \
proclang.o \
propgraphcmds.o \
publicationcmds.o \
+ repack.o \
schemacmds.o \
seclabel.o \
sequence.o \
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index d3be8939011..5db4fe75dce 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -24,8 +24,8 @@
#include "catalog/namespace.h"
#include "catalog/pg_am.h"
#include "catalog/pg_opclass.h"
-#include "commands/cluster.h"
#include "commands/matview.h"
+#include "commands/repack.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "executor/executor.h"
diff --git a/src/backend/commands/meson.build b/src/backend/commands/meson.build
index 90c7e37a429..f624aae74af 100644
--- a/src/backend/commands/meson.build
+++ b/src/backend/commands/meson.build
@@ -6,7 +6,6 @@ backend_sources += files(
'amcmds.c',
'analyze.c',
'async.c',
- 'cluster.c',
'collationcmds.c',
'comment.c',
'constraint.c',
@@ -39,6 +38,7 @@ backend_sources += files(
'proclang.c',
'propgraphcmds.c',
'publicationcmds.c',
+ 'repack.c',
'schemacmds.c',
'seclabel.c',
'sequence.c',
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/repack.c
similarity index 99%
rename from src/backend/commands/cluster.c
rename to src/backend/commands/repack.c
index f241e18b153..20f0a572236 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/repack.c
@@ -1,6 +1,6 @@
/*-------------------------------------------------------------------------
*
- * cluster.c
+ * repack.c
* REPACK a table; formerly known as CLUSTER. VACUUM FULL also uses
* parts of this code.
*
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * src/backend/commands/cluster.c
+ * src/backend/commands/repack.c
*
*-------------------------------------------------------------------------
*/
@@ -33,9 +33,9 @@
#include "catalog/pg_am.h"
#include "catalog/pg_inherits.h"
#include "catalog/toasting.h"
-#include "commands/cluster.h"
#include "commands/defrem.h"
#include "commands/progress.h"
+#include "commands/repack.h"
#include "commands/tablecmds.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0ce2e81f9c2..e2882a50b3b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -57,10 +57,10 @@
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "catalog/toasting.h"
-#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
+#include "commands/repack.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 0ed363d1c85..b179b62b5c8 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -9,7 +9,7 @@
*
* VACUUM for heap AM is implemented in vacuumlazy.c, parallel vacuum in
* vacuumparallel.c, ANALYZE in analyze.c, and VACUUM FULL is a variant of
- * CLUSTER, handled in cluster.c.
+ * REPACK, handled in repack.c.
*
*
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
@@ -38,9 +38,9 @@
#include "catalog/pg_database.h"
#include "catalog/pg_inherits.h"
#include "commands/async.h"
-#include "commands/cluster.h"
#include "commands/defrem.h"
#include "commands/progress.h"
+#include "commands/repack.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -2293,7 +2293,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params,
if ((params.options & VACOPT_VERBOSE) != 0)
cluster_params.options |= CLUOPT_VERBOSE;
- /* VACUUM FULL is a variant of REPACK; see cluster.c */
+ /* VACUUM FULL is a variant of REPACK; see repack.c */
cluster_rel(REPACK_COMMAND_VACUUMFULL, rel, InvalidOid,
&cluster_params);
/* cluster_rel closes the relation, but keeps lock */
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 7e017c8d53b..7cef6e43661 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -19,6 +19,7 @@
#include "access/parallel.h"
#include "commands/async.h"
+#include "commands/repack.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "port/pg_bitutils.h"
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 10be60011ad..9fbaa5c00f0 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -39,6 +39,7 @@
#include "commands/event_trigger.h"
#include "commands/explain_state.h"
#include "commands/prepare.h"
+#include "commands/repack.h"
#include "common/pg_prng.h"
#include "jit/jit.h"
#include "libpq/libpq.h"
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 2b609bfc824..5f8c766c4be 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -26,7 +26,6 @@
#include "catalog/toasting.h"
#include "commands/alter.h"
#include "commands/async.h"
-#include "commands/cluster.h"
#include "commands/collationcmds.h"
#include "commands/comment.h"
#include "commands/conversioncmds.h"
@@ -46,6 +45,7 @@
#include "commands/proclang.h"
#include "commands/propgraphcmds.h"
#include "commands/publicationcmds.h"
+#include "commands/repack.h"
#include "commands/schemacmds.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
diff --git a/src/include/commands/cluster.h b/src/include/commands/repack.h
similarity index 90%
rename from src/include/commands/cluster.h
rename to src/include/commands/repack.h
index d6b62c747e8..85061158b0c 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/repack.h
@@ -1,17 +1,17 @@
/*-------------------------------------------------------------------------
*
- * cluster.h
- * header file for postgres cluster command stuff
+ * repack.h
+ * header file for the REPACK command
*
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
- * src/include/commands/cluster.h
+ * src/include/commands/repack.h
*
*-------------------------------------------------------------------------
*/
-#ifndef CLUSTER_H
-#define CLUSTER_H
+#ifndef REPACK_H
+#define REPACK_H
#include "nodes/parsenodes.h"
#include "parser/parse_node.h"
@@ -52,4 +52,4 @@ extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
MultiXactId cutoffMulti,
char newrelpersistence);
-#endif /* CLUSTER_H */
+#endif /* REPACK_H */
--
2.47.3
--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="v50-0003-Add-CONCURRENTLY-option-to-REPACK-command.patch"
^ permalink raw reply [nested|flat] 319+ messages in thread
end of thread, other threads:[~2026-03-31 16:55 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-31 16:55 [PATCH v50 2/8] Rename cluster.c/h -> repack.c/h Álvaro Herrera <alvherre@kurilemu.de>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox